home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.cs.arizona.edu
/
ftp.cs.arizona.edu.tar
/
ftp.cs.arizona.edu
/
icon
/
newsgrp
/
group93b.txt
/
000024_icon-group-sender _Sun Apr 25 22:08:38 1993.msg
< prev
next >
Wrap
Internet Message Format
|
1993-06-16
|
6KB
Received: by cheltenham.cs.arizona.edu; Mon, 26 Apr 1993 04:31:36 MST
Message-Id: <9304260508.AA21290@internal.apple.com>
Date: Sun, 25 Apr 1993 22:08:38 -0800
To: icon-group@cs.arizona.edu
From: nevin@apple.com (Nevin ":-]" Liber)
X-Sender: nevin@130.43.2.2
Subject: Re: Is this passable code?
Cc: glauber@david.wheaton.edu (Glauber Ribeiro)
Status: R
Errors-To: icon-group-errors@cs.arizona.edu
>I started using Icon recently, and am impressed with the
>power of the language. But, since i'm learning on my own and
>don't have experience with Icon, i don't know if i'm doing
>things right. Recently i wrote a short program to find
>anagrams (yeah...) using the unix dictionary. I'd appreciate
>any comments/suggestions/flames, etc about my programming
>style and the Icon way.
I did think of a different (simpler, I believe) way of implementing the
anagram algorithm, using sets. I don't know if it's better or worse, but
it does illustrate my particular style for programming in Icon. Here it
is, along with an explanation of some of my stylistic choices:
# look up anagrams of all the command line arguments in /usr/dict/words
procedure main(LArguments)
local SAnagrams
local fWords
local sWord
SAnagrams := set()
every insert(SAnagrams, Permute(map(!LArguments)))
fWords := open("/usr/dict/words") |
stop("Cannot open /usr/dict/words for reading; aborting.")
while sWord := read(fWords) do {
if member(SAnagrams, map(sWord)) then write(sWord)
}
close(fWords)
end
procedure Permute(sString)
local iPos
if 0 = *sString then return sString
suspend sString[iPos := 1 to *sString] ||
Permute(sString[1:iPos] || sString[iPos+1:0])
end
Variable conventions: In my code, the first letter of each variable
identifies what kind of value it contains, following the convention on the
first page of the Reference Manual in the Icon book. In this program, L
stands for list, S for set, f for file, s for string, and i for integer. I
also try and give full names to variables to make the code more readable
and understandable. I also capitalise the first letter of each word (an
Apple convention for code, which I like much, much better than using
underscores), as well as starting all of my functions with a capital
letter. I tend to declare all of my local variables, so that icont -u can
catch an inadvertent spelling errors.
Permute(): This is a recursive generator which produces all possible
permutations of its argument. I tend to let the caller (in this case,
main()) build the container structure (a set) instead of the callee because
in many cases the results of a generator can be used directly without
having to build a large data structure in memory.
Important points about main():
every insert(SAnagrams, Permute(map(!LArguments))): "every" generates all
possible values for the insert(...) expression. Let's analyse this from
inside to out:
every !LArguments: this produces all the members of list LArguments, in
order. In this case, this produces strings of all the command line
arguments. If there are no arguments, it fails; failure is inherited by
the rest of the expression, and the set SAnagrams remains empty.
every map(!LArguments): When I use map() with it's default arguments for
s2 (source mapping string) and s3 (destination mapping string), I think of
it as "removing" the caseness of its argument; I don't really care if it
converts it to all uppercase or all lowercase.
every Permute(map(!LArguments)): This produces all the possible "caseless"
permutations of all the command line arguments. Although this is logically
equivalent to every map(Permute(!LArguments)), I chose to put it inside of
Permute(...) because it is more efficient to call it once per command line
argument than to call it for each permutation.
every insert(SAnagrams, Permute(map(!LArguments))): This produces a set of
all the possible "caseless" permutations of all the command line arguments.
If there are duplicate permutations (if a given letter appears more than
once in a word, there will be duplicate permutations), they are
automatically discarded since we are using a set data type.
fWords := open("/usr/dict/words") |
stop("Cannot open /usr/dict/words for reading; aborting."):
I prefer to use alternation (|) instead of an if statement. I think of the
open() as the normal expected case, and the stop() clause as the
"exception".
if member(SAnagrams, map(sWord)) then write(sWord): Again, I do a
map(sWord) to remove the caseness of SWord so I can do a caseless
comparison. If it's in the set of possible anagrams, I write out the word,
preserving the case it has in /usr/dict/words.
Variations on this algorithm: There are 24259 words in /usr/dict/words on
my Unix machine. If I was expecting to process many words with 8 or more
unique letters (8! = 40320 permutations), I would have approached this a
little differently. I would have read /usr/dict/words into a set (or a
table, if I thought that preserving case was important), and do something
like [note: if you want to preserve case, it's actually a little harder
than this. The algorithm below ignores words in /usr/dict/words that only
differ by case, and it probably shouldn't]:
procedure main(LArguments)
local TWords
local fWords
local sWord
TWords := table()
fWords := open("/usr/dict/words") |
stop("Cannot open /usr/dict/words for reading; aborting.")
while sWord := read(fWords) do TWords[map(sWord)] := sWord
close(fWords)
every write(\TWords[Permute(map(!LArguments))])
end
I would probably even come up with a heuristic that analyses LArguments and
determines which of the two above variations to call to do the work.
I hope this is what you were looking for. I can't believe I wrote this
much on a 20 line algorithm. :-) Comments are appreciated.
___
NEVIN ":-)" LIBER, Blue Meanie, Macintosh System Software
email: nevin@apple.com paper: Apple Computer, Inc.
voice: (408) 974-MIX1 2 Infinite Loop, MS: 302-4Q
AppleLink: BADENOV Cupertino, CA 95014